﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data.SqlClient;

namespace TUtils
{
	/// <summary>
	/// <see cref="TUtils.TExceptionHelper.DumpException"/>
	/// </summary>
	[AttributeUsage(AttributeTargets.Property)] 
	public class TLoggingAdditionalInfoAttribute : Attribute
	{
	}

	/// <summary>
	/// Hilfsklasse für Exceptionhandling
	/// </summary>
	public class TExceptionHelper
	{
		/// <summary>
		/// Erzeugt aus einer Exception einen lesbaren Fehlerlogeintrag.<br />
		/// Properties von Exceptionklassen, die mit einem 
		/// <see cref="TUtils.TLoggingAdditionalInfoAttribute"/> Attribut
		/// versehen sind, werden automatisch mit geloggt.
		/// </summary>
		/// <param name="e"></param>
		/// <returns></returns>
		public static string DumpException(Exception e)
		{
			StringBuilder buf = new StringBuilder();
			buf.AppendFormat("ERROR: \n");
			Exception eCur = e;
			while (eCur != null)
			{
				buf.AppendFormat("Exception {0} was thrown\n", eCur.GetType().FullName);
				buf.AppendFormat("Message: {0}\n", eCur.Message);
				buf.AppendFormat(
					"Stack: {0}\n",
					eCur.StackTrace);

				PropertyInfo[] properties = eCur.GetType().GetProperties();
				foreach (PropertyInfo pi in properties)
				{
					object[] attributes = pi.GetCustomAttributes(typeof(TLoggingAdditionalInfoAttribute),true);
					foreach (object attr in attributes)
					{
						try
						{
							object val = pi.GetValue(eCur, null);
							string strVal = val == null ? "null" : val.ToString();
							buf.AppendFormat("{0}={1}\n", pi.Name, strVal);
							break;
						}
						catch
						{
							buf.Append("..\n");
						}
					}
				}

				buf.AppendFormat("...............................................\n");

				eCur = eCur.InnerException;
			}
			return buf.ToString();
		}

		/// <summary>
		/// Prüft für die gegebene Exception und alle inneren Exceptions,
		/// ob sie dem gegebenen Typ entsprechen und gibt die erste gefundene
		/// Entsprechung zurück, sonst null.
		/// </summary>
		/// <param name="ex"></param>
		/// <param name="innerExceptionType"></param>
		/// <returns></returns>
		public static Exception FindInnerException(Exception ex, Type innerExceptionType)
		{
			if (ex.GetType().Equals(innerExceptionType))
			{
				return ex;
			}
			else
			{
				if (ex.InnerException != null)
					return FindInnerException(ex.InnerException, innerExceptionType);
			}
			return null;
		}

		/// <summary>
		/// Liefert true zurück, wenn die geg. exception oder eine innere
		/// Exception auf eine verletzung eines Unique key Constraints hindeutet
		/// </summary>
		/// <param name="ex"></param>
		/// <returns></returns>
		public static bool HasSqlException_UniqueKeyViolation(Exception ex)
		{
			Exception e = FindInnerException(ex, typeof(SqlException));
			if (e != null)
			{
				SqlException es = (SqlException)e;
				if (es.Number == 2601)
					return true;
			}
			return false;
		}
	}
}
